
#include "core/olisp_core.h"
#include "core/olisp_mem.h"


typedef struct
{
    num_obj_vtbl_t * vtbl;

    int ref_count;
    
    // 0 - is int, 1 - is double
    int is_double;
    
    union
    {
        int int_val;
        double dbl_val;
    } number;
} num_impl_t;

static int num_add_ref( num_obj_t * self )
{
    num_impl_t * s = (num_impl_t *)self;
    return ++s->ref_count;
}

static int num_release( num_obj_t * self )
{
    num_impl_t * s = (num_impl_t *)self;
    int res = --s->ref_count;
    
    if( res > 0 )
        return res;

    lisp_free( s );
    return 0;
}

static int num_as_int( num_obj_t * self )
{
    num_impl_t * s = (num_impl_t *)self;
    if( s->is_double )
    {
        return (int)s->number.dbl_val;
    }
        
    return s->number.int_val;
}

static double num_as_double( num_obj_t * self )
{
    num_impl_t * s = (num_impl_t *)self;
    if( s->is_double )
    {
        return s->number.dbl_val;
    }
        
    return (double)s->number.int_val;
}

static int num_is_double( num_obj_t * self )
{
    num_impl_t * s = (num_impl_t *)self;
    return s->is_double;
}


static num_obj_vtbl_t num_obj_vtbl =
{
    LISP_ELEM_NUMBER,
    &num_add_ref,
    &num_release,

    &num_as_int,
    &num_as_double,
    &num_is_double
};

static num_impl_t * create_number_elem()
{
    num_impl_t * s = lisp_malloc( sizeof(num_impl_t) );
    if( s )
    {
        s->ref_count = 1;
        s->vtbl = &num_obj_vtbl;
    }
    return s;
}

num_obj_t * create_int_number( int num )
{
    num_impl_t * s = create_number_elem();
    if( s )
    {
        // initialize as integer number
        s->number.int_val = num;
        s->is_double = 0;
    }
    return (num_obj_t *)s;
}

num_obj_t * create_dbl_number( double num )
{
    num_impl_t * s = create_number_elem();
    if( s )
    {
        // initialize as integer number
        s->number.dbl_val = num;
        s->is_double = 1;
    }
    return (num_obj_t *)s;
}
